package com.simple3d.primitives
{
	import __AS3__.vec.Vector;
	
	import flash.display.BitmapData;
	import flash.display.Sprite;
	import flash.geom.Matrix3D;
	import flash.geom.Utils3D;
	import flash.geom.Vector3D;
	
	/**
	 * The cube class lets you render a cube.
	 * @author Pradeek 
	 */	
	public class Cube extends Sprite
	{	
		private var displayMode:uint = 0;

		private var topPlane:Object = new Object();
		private var bottomPlane:Object = new Object();
		private var leftPlane:Object = new Object();
		private var rightPlane:Object = new Object();
		private var frontPlane:Object = new Object();
		private var backPlane:Object = new Object();
		
		protected var vertices:Vector.<Number>;
		protected var projectedVertices:Vector.<Number>;
		protected var indices:Vector.<int>;
		protected var uvtData:Vector.<Number>;
		protected var matrix3d:Matrix3D;
		
		/**
		 *  Indicates the side of the cube, in pixels.
		 */		
		public var side:Number = 100;
		
		/**
		 * Indicates the color of the cube. 
		 */		
		public var color:uint = 0x000000;
		
		/**
		 * Indicates the BitmapData of the texture of the cube. 
		 */		
		public var texture:BitmapData;
		
		/**
		 * Constructor
		 * @param side - Size of the side of the cube
		 * @param texture - BitmapData of the texture.
		 */		
		public function Cube(side:Number = 100, texture:BitmapData = null)
		{
			this.side = side;
			this.texture = texture;
			
			if(texture)
			{
				displayMode = 1;
			}
			
			vertices = new Vector.<Number>();
			projectedVertices = new Vector.<Number>();
			indices = new Vector.<int>();
			uvtData = new Vector.<Number>();			
			matrix3d = new Matrix3D();
						
			indices.push(0,1,2);
			indices.push(2,3,0);								
							
			uvtData.push(0,0,0);
			uvtData.push(1,0,0);
			uvtData.push(1,1,0);
			uvtData.push(0,1,0);
			
			buildTopPlane();
			buildBottomPlane();
			buildFrontPlane();
			buildBackPlane();
			buildLeftPlane();
			buildRightPlane();
		}
				
		private function buildLeftPlane():void
		{
			vertices = new Vector.<Number>();
			
			vertices.push(-this.side/2, -this.side/2, -this.side/2); //v000
			vertices.push(-this.side/2, -this.side/2, +this.side/2); //v001
			vertices.push(-this.side/2, +this.side/2, +this.side/2); //v011
			vertices.push(-this.side/2, +this.side/2, -this.side/2); //v010
			
			leftPlane.vertices = vertices;
			leftPlane.projectedVertices = new Vector.<Number>();	
		}		
		
		private function buildRightPlane():void
		{
			vertices = new Vector.<Number>();
			
			vertices.push(+this.side/2, -this.side/2, -this.side/2); //v100
			vertices.push(+this.side/2, -this.side/2, +this.side/2); //v101
			vertices.push(+this.side/2, +this.side/2, +this.side/2); //v111
			vertices.push(+this.side/2, +this.side/2, -this.side/2); //v110
			
			rightPlane.vertices = vertices;
			rightPlane.projectedVertices = new Vector.<Number>();	
		}
						
		private function buildTopPlane():void
		{
			vertices = new Vector.<Number>();
			
			vertices.push(-this.side/2, +this.side/2, +this.side/2); //v011
			vertices.push(-this.side/2, +this.side/2, -this.side/2); //v010
			vertices.push(+this.side/2, +this.side/2, -this.side/2); //v110
			vertices.push(+this.side/2, +this.side/2, +this.side/2); //v111
			
			topPlane.vertices = vertices;
			topPlane.projectedVertices = new Vector.<Number>();
		}		
				
		private function buildBottomPlane():void
		{
			vertices = new Vector.<Number>();
			
			vertices.push(-this.side/2, -this.side/2, -this.side/2); //v000
			vertices.push(-this.side/2, -this.side/2, +this.side/2); //v001
			vertices.push(+this.side/2, -this.side/2, +this.side/2); //v101
			vertices.push(+this.side/2, -this.side/2, -this.side/2); //v100
			
			bottomPlane.vertices = vertices;
			bottomPlane.projectedVertices = new Vector.<Number>();
		}		
		
		private function buildFrontPlane():void
		{
			vertices = new Vector.<Number>();
			
			vertices.push(-this.side/2, -this.side/2, -this.side/2); //v000
			vertices.push(+this.side/2, -this.side/2, -this.side/2); //v100
			vertices.push(+this.side/2, +this.side/2, -this.side/2); //v110
			vertices.push(-this.side/2, +this.side/2, -this.side/2); //v010
			
			frontPlane.vertices = vertices;
			frontPlane.projectedVertices = new Vector.<Number>();
		}		
				
		private function buildBackPlane():void
		{
			vertices = new Vector.<Number>();
			
			vertices.push(-this.side/2, -this.side/2, +this.side/2); //v001
			vertices.push(-this.side/2, +this.side/2, +this.side/2); //v011
			vertices.push(-this.side/2, +this.side/2, +this.side/2); //v011
			vertices.push(+this.side/2, -this.side/2, +this.side/2); //v101
			
			backPlane.vertices = vertices;
			backPlane.projectedVertices = new Vector.<Number>();
		}		
		
		/**
		 *  Renders the cube. 
		 */		
		public function render():void
		{
			this.graphics.clear();
				
			this.renderPlane(topPlane);
			this.renderPlane(bottomPlane);
			this.renderPlane(frontPlane);
			this.renderPlane(backPlane);
			this.renderPlane(leftPlane);
			this.renderPlane(rightPlane);
		}
			
		protected function renderPlane(plane:Object):void
		{
			Utils3D.projectVectors(matrix3d,plane.vertices,plane.projectedVertices,uvtData);
			
			switch(displayMode)
			{
				case 0:
					this.graphics.beginFill(color);
					this.graphics.lineStyle(1,color);
					this.graphics.drawTriangles(plane.projectedVertices,indices);
					break;
				case 1:
					this.graphics.beginBitmapFill(this.texture,null,false,false);
					this.graphics.drawTriangles(plane.projectedVertices,indices,uvtData);
					break;
			}
			this.graphics.endFill();				
		}
		
		/**
		 *  Rotate the 3D object on all axes.
		 *  @param value - amount of rotation.
		 */		
		public function rotate(value:Number):void
		{  
			matrix3d.appendRotation(value,Vector3D.X_AXIS);
			matrix3d.appendRotation(value,Vector3D.Y_AXIS);
			matrix3d.appendRotation(value,Vector3D.Z_AXIS);
		}		

		public override function set rotationX(value:Number):void
		{
			matrix3d.appendRotation(value,Vector3D.X_AXIS);
		}
		
		public override function set rotationY(value:Number):void
		{
			matrix3d.appendRotation(value,Vector3D.Y_AXIS);
		}
		
		public override function set rotationZ(value:Number):void
		{
			matrix3d.appendRotation(value,Vector3D.Z_AXIS);
		}
	}
}